/*
 * mke_err.c - mkentity error handling related
 * cezanne@clunix.com, 2007.1
 *
 * Copyright (c) 2001-2007 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "mkentity.h"
#include "mke_err.h"
#include "mke_utils.h"

static ecm_uint32	n_errors[MKE_ERROR_EOF];

const char *err_type_strs[] = {
	"error",
	"warning"
};

static void
update_error_count(mke_err_t type)
{
	n_errors[type]++;
}

static char *
get_funcsig_desc(const sr_t *sr)
{
	sr_t	*sr_arg;
	char	*desc, *args = NULL, *retstr = NULL;

	SR_FOREACH_CHILD(sr, "args", sr_arg) {
		char	*ettstr;

		ettstr = sr_ETT_to_str(sr_arg);
		if (args) {
			char	*tmp;

			ecm_asprintf(&tmp, "%s,%s", args, ettstr);
			ecm_free(args);
			args = tmp;
		}
		else
			args = ettstr;
	}
	retstr = sr_ETT_to_str(sr_getc(sr, "ret"));
	ecm_asprintf(&desc, "%s%s(%s)", ecm_safe_str(retstr), sr_get_name(sr), ecm_safe_str(args));
	ecm_free(args);
	ecm_free(retstr);
	return desc;
}

static char *
get_desc(const sr_t *sr)
{
	srt_t	srt;
	char	*desc = NULL;

	srt = sr_get_type(sr);

	if (srt == ST_MKE_NULL)
		desc = ecm_strdup("<null>");
	if (srt == ST_MKE_LITERAL)
		desc = ecm_strdup(sr_getc_str(sr, "literal"));
	else if (srt == ST_MKE_ETT)
		desc = sr_ETT_to_str(sr);
	else if (srt == ST_MKE_CTXCLS_NAME)
		desc = ecm_strdup(sr_getc_str(sr, "clsname"));
	else if (srt == ST_MKE_CLASS)
		ecm_asprintf(&desc, "<@%s>", sr_get_name(sr));
	else if (srt == ST_MKE_CONTEXT)
		ecm_asprintf(&desc, "<%s@%s>", sr_get_name(sr), sr_getc_str(sr, "clsname"));
	else if (srt == ST_MKE_ASRTV) {
		char	*ettstr = sr_ETT_to_str(sr_geti(sr, 0));
		ecm_asprintf(&desc, "%s%s", ettstr, sr_geti_str(sr, 1));
		ecm_free(ettstr);
	}
	else if (srt == ST_MKE_REACTORS) {
		char	*edexprs;

		/* TODO: */
		edexprs = edl_sr_to_edexpr(sr_getc(sr, "edexprs"));
		ecm_asprintf(&desc, "[%s]", edexprs);
		ecm_free(edexprs);
	}
	else if (srt == ST_MKE_ETT_DEF) {
		const char	*name = sr_getc_str(sr, "name");

		if (ecm_empty_str(name))
			desc = sr_ETT_to_str(sr_getc(sr, "ETT"));
		else {
			char	*srtstr = sr_ETT_to_str(sr_getc(sr, "ETT"));
			ecm_asprintf(&desc, "%s %s", name, srtstr);
			ecm_free(srtstr);
		}
	}
	else if (srt == ST_MKE_EPE_DECL || srt == ST_MKE_EDE_DECL)
		desc = sr_ETT_to_str(sr_getc(sr_getc(sr, "def"), "ETT"));
	else if (srt == ST_MKE_EMBED)
		ecm_asprintf(&desc, "[%s]", sr_get_name(sr));
	else if (srt == ST_MKE_FUNCSIG)
		desc = get_funcsig_desc(sr);
	return desc;
}

static void
vprint_errmsg(mke_err_t type, const sr_t *sr, ecm_uint32 lineno, const char *fmt, va_list ap)
{
	char	*buf, *desc;
	const char	*src = NULL;
	const char	*msgtype = err_type_strs[type];
	ecm_uint32	line;

	desc = get_desc(sr);

	src = sr_getc_str(sr, "srcname");
	line = sr_getc_int(sr, "lineno") + lineno;
	if (ecm_empty_str(src)) {
		sr_t	*sr_srcinfo = sr_get_ext(sr);

		src = sr_getc_str(sr_srcinfo, "srcname");
		line = sr_getc_int(sr_srcinfo, "lineno") + lineno;
	}

	ecm_vasprintf(&buf, fmt, ap);
	if (src == NULL)
		fprintf(stderr, "%s: %s\n", msgtype, buf);
	else {
#ifndef _WIN32
		if (ecm_empty_str(desc))
			fprintf(stderr, "%s:%u:%s: %s\n",
				ecm_safe_str(src), line, msgtype, buf);
		else
			fprintf(stderr, "%s:%u:%s:%s: %s\n",
				ecm_safe_str(src), line, msgtype, desc, buf);
#else
		if (ecm_empty_str(desc))
			fprintf(stderr, "%s(%u):%s: %s\n",
				ecm_safe_str(src), line, msgtype, buf);
		else
			fprintf(stderr, "%s(%u):%s:%s: %s\n",
				ecm_safe_str(src), line, msgtype, desc, buf);
#endif
	}
	ecm_free(buf);
	ecm_free(desc);

	update_error_count(type);
}

void
print_error(mke_err_t type, const sr_t *sr, ecm_uint32 lineno, const char *fmt, ...)
{
	va_list	ap;

	if (fmt == NULL)
		return;
	va_start(ap, fmt);
	vprint_errmsg(type, sr, lineno, fmt, ap);
	va_end(ap);
}

void
raise_error(mke_err_t type, ecm_uint32 lineno, const char *fmt, ...)
{
	va_list	ap;
	char	*buf;
	const char	*typestr = err_type_strs[type];

	va_start(ap, fmt);
	ecm_vasprintf(&buf, fmt, ap);
	va_end(ap);
#ifndef _WIN32
	fprintf(stderr, "%s:%u: %s: %s\n", specsrc, lineno, typestr, buf);
#else
	fprintf(stderr, "%s(%u): %s: %s\n", specsrc, lineno, typestr, buf);
#endif
	ecm_free(buf);

	update_error_count(type);
}

void
add_src_info(sr_t *sr, ecm_uint32 lineno)
{
	sr_createc_str(sr, "srcname", ST_STR, specsrc);
	sr_createc_int(sr, "lineno", ST_INT, lineno);
}

void
add_srcinfo_ext(sr_t *sr, ecm_uint32 lineno)
{
	sr_t	*sr_srcinfo;

	if (sr == NULL)
		return;
	sr_srcinfo = sr_create_dir();
	sr_createc_str(sr_srcinfo, "srcname", ST_STR, specsrc);
	sr_createc_int(sr_srcinfo, "lineno", ST_INT, lineno);
	sr_set_ext(sr, sr_srcinfo);
}

ecm_uint32
get_error_count(mke_err_t type)
{
	return n_errors[type];
}

